<template>
  <div :class="$style.page" class="flex">
    <!--  -->
    <t-query
      :class="$style.query"
      v-model="pageOpts.filters"
      :province="pageOpts.provinceList"
      :city="pageOpts.cityList"
      :district="pageOpts.districtList"
      :street="pageOpts.streetList"
      @download="onDownload"
      @change="onQueryChange"
    ></t-query>

    <!-- tip -->
    <div :class="$style.tip" v-show="pageOpts.tips.length">
      <div :class="$style.close">
        <el-link :underline="false" @click="onCloseTip">
          <icon-ep-close></icon-ep-close>
        </el-link>
      </div>
      <div :class="$style.wrap" ref="tipRef">
        <p v-for="text in pageOpts.tips">{{ text }}</p>
      </div>
    </div>

    <!-- map -->
    <div :class="$style.map" id="map" class="f-x"></div>

    <!-- echarts -->
    <div :class="$style.echarts" class="f-x">
      <div ref="echartsRef"></div>
    </div>

    <t-dialog v-model="visible"></t-dialog>
  </div>
</template>

<script lang="ts" setup>
import tQuery from './components/query/index.vue'
import tDialog from './components/dialog/index.vue'
import { getPageOpts } from './data'
import * as MD from './methods'
import { Echarts, echarts } from '@/echarts'
import { useResize } from '@/echarts/resize'
import { Message } from '@/utils/element'

const pageOpts = reactive(getPageOpts())
const tipRef = ref()
const visible = ref(false)

// 查询
const onQueryChange = () => {
  clearPolygons()
  const { province, city, district } = pageOpts.filters

  // 判断级别
  let level = 'country'
  let name = pageOpts.mapName
  // 省份
  if (!district && !city) {
    level = 'province'
    pageOpts.mapCode = province
    name = pageOpts.provinceList.find(it => it.code == province)?.name || ''
  }
  // 地级市
  else if (!district) {
    level = 'city'
    pageOpts.mapCode = city
    name = pageOpts.cityList.find(it => it.code == city)?.name || ''
  }
  // 区县
  else {
    level = 'district'
    pageOpts.mapCode = district
    name = pageOpts.districtList.find(it => it.code == district)?.name || ''
  }

  // 当去区域名称
  pageOpts.mapName = name

  // 可选值： country：国家 province：省/直辖市 city：市 district：区/县 biz_area：商圈
  districtSearch.setLevel(level)

  districtSearch.setExtensions('all')
  districtSearch.search(pageOpts.mapCode, (status: string, result: AnyObject) => {
    if (status === 'complete') {
      getData(result.districtList[0], level, pageOpts.mapCode)
    }
  })
}

// 关闭提示
const onCloseTip = () => {
  pageOpts.tips = []
}

// 下载
const onDownload = () => {
  const way = pageOpts.filters.downloadWay

  if (way == 'all') {
    downloadAllJson()
    return
  } else if (way == 'area') {
    visible.value = true
    return
  }

  let filename = pageOpts.mapName
  if (way == 'code') {
    filename = pageOpts.mapCode + ''
  }
  MD.fileSave('geoJson', JSON.stringify(pageOpts.geoJsonData), filename)
}

const downloadList = ref<ListItem[]>([])
const count = ref(0)
// 下载所有数据
const downloadAllJson = () => {
  downloadList.value = []

  loadedNode = {}
  waitToLoadList = []

  pageOpts.tips = []
  count.value = 0
  pageOpts.tips.push('开始获取地区code')
  districtSearch.setExtensions('all')
  // 循环查询地图数据
  loopSearchFilter('中国')
}

// 判断是否加载过变量
let loadedNode: AnyObject = {}
let waitToLoadList: (number | string)[] = []
const loopSearchFilter = (areaCode: string | number) => {
  if (loadedNode[areaCode]) return console.warn('已加载过', areaCode)

  waitToLoadList.push(areaCode)
  loopSearchMap()
}

// 线程-高德并发限制数量 3
let loadingThreads = new Array(3).fill(null)
// 循环查询数据
const loopSearchMap = () => {
  // 闲置线程
  const idleThread = loadingThreads.findIndex(i => !i)
  // 没有空闲不处理
  if (idleThread < 0) {
    return
  }

  // 当前加载 code
  // shiift - 删除第一个并返回
  const code = waitToLoadList.shift()

  // 当 code 不存在，则加载完成
  if (!code) {
    // 当前使用线程
    const activeThread = loadingThreads.find(i => i)
    if (!activeThread) {
      MD.downloadListGeoJson(downloadList.value, map, (text: string) => {
        pageOpts.tips.push(text)
        tipRef.value.scrollTop = 100000
      })
    }
    return
  }

  // 标记加载对象
  loadedNode[code] = true

  loadingThreads[idleThread] = new Promise((resolve, reject) => {
    districtSearch.search(code, (status: string, result: AnyObject) => {
      if (status !== 'complete') {
        return reject(status)
      }
      count.value++
      pageOpts.tips.push(
        `线程- ${idleThread + 1}/${loadingThreads.length}: ${code} - count: ${count.value}`
      )
      tipRef.value.scrollTop = 100000
      const item = result.districtList[0]
      const list = item.districtList

      list &&
        list.forEach((item: AnyObject) => {
          const { name, adcode, level } = item
          downloadList.value.push({ name, code: adcode, type: level })

          if (!['city', 'district', 'street'].includes(level)) loopSearchFilter(adcode)
        })

      // 高德并发限制时间 1 秒
      setTimeout(() => {
        resolve(code)
      }, 1000)
    })
  })
    .catch(() => {
      // 标记未成功 继续加载
      loadedNode[code] = false
      loopSearchFilter(code)
    })
    // 异步 无论成功/失败 都执行
    .finally(() => {
      // 设置线程可用
      loadingThreads[idleThread] = null
      loopSearchMap()
    })
}

const mapData = ref<AnyObject[]>([])
const echartsRef = ref()
let echrtsObj: ZEcharts
const initEcharts = (mapJson: Parameters<typeof echarts.registerMap>[1]) => {
  const dom = echartsRef.value
  if (!dom) return

  const options = MD.getEcOPts(pageOpts.mapName, mapData.value)
  if (echrtsObj) {
    echrtsObj.Echarts.dispose()
  }
  echarts.registerMap(pageOpts.mapName, mapJson)
  echrtsObj = new Echarts(dom, options, 'light')
  useResize(echrtsObj).resize()

  echrtsObj.Echarts.on('click', params => {
    const data: any = params.data
    // 街道不处理
    if (data.level == 'street') return

    clearPolygons()

    const areaCode = data.cityCode
    const level = data.level
    pageOpts.mapName = data.name
    pageOpts.mapCode = areaCode
    districtSearch.setLevel(level) //行政区级别
    if (level == 'province') {
      pageOpts.filters.province = areaCode
    } else if (level == 'city') {
      pageOpts.filters.city = areaCode
    } else {
      pageOpts.filters.district = areaCode
    }

    districtSearch.setExtensions('all')
    //行政区查询
    //按照adcode进行查询可以保证数据返回的唯一性
    districtSearch.search(areaCode, (status: string, result: AnyObject) => {
      if (status === 'complete') {
        getData(result.districtList[0], level, areaCode)
      }
    })
  })
}

// 清除覆盖物
const clearPolygons = () => {
  //清除地图上所有覆盖物
  for (let i = 0, l = pageOpts.polygons.length; i < l; i++) {
    pageOpts.polygons[i].setMap(null)
  }
}

// 加载地图数据
const loadMapData = (areaCode: number) => {
  MD.loadAreacodeData(map, areaCode).then(mapJson => {
    initEcharts(mapJson)
    // @ts-ignore
    pageOpts.geoJsonData = mapJson
  })
}

// 获取数据
const getData = (data: AnyObject, levle: string, adcode: number) => {
  const bounds = data.boundaries

  // 边界
  if (bounds) {
    // @ts-ignore
    const AMap = window.AMap
    for (let i = 0, l = bounds.length; i < l; i++) {
      let polygon = new AMap.Polygon({
        map: map,
        strokeWeight: 1,
        strokeColor: '#0091ea',
        fillColor: '#80d8ff',
        fillOpacity: 0.2,
        path: bounds[i]
      })
      pageOpts.polygons.push(polygon)
    }
    //地图自适应
    map.setFitView()
  }

  // 下级列表
  const subList = data.districtList
  if (!subList) return
  const curlevel = subList[0].level

  // 街道
  if (curlevel === 'street') {
    const mapJsonList = pageOpts.geoJsonData.features
    let mapJson: Parameters<typeof echarts.registerMap>[1] = {
      type: 'FeatureCollection',
      features: []
    }
    for (let i in mapJsonList) {
      if (mapJsonList[i].properties.name == pageOpts.mapName) {
        mapJson.features = [].concat(mapJsonList[i])
      }
    }
    mapData.value = [
      {
        name: pageOpts.mapName,
        value: Math.random() * 100,
        level: curlevel
      }
    ]
    initEcharts(mapJson)
    return
  }

  const mapList = []
  mapData.value = []
  for (let i = 0; i < subList.length; i++) {
    const name = subList[i].name
    const cityCode = subList[i].adcode

    // 地图随机数据
    mapData.value.push({
      name: name,
      value: Math.random() * 100,
      cityCode: cityCode,
      level: curlevel
    })

    // 省份数据
    mapList.push({
      name,
      code: subList[i].adcode,
      center: subList[i].center
    })
  }

  // 省份
  if (levle == 'province') {
    pageOpts.cityList = mapList
  }
  // 地级市
  else if (levle == 'city') {
    pageOpts.districtList = mapList
  }
  // 区县
  else if (levle == 'district') {
    pageOpts.streetList = mapList
  } else {
    pageOpts.provinceList = mapList
  }

  loadMapData(adcode)
}

let map: AnyObject
// 行政区查询、天气查询、公交站点和公交线路查询
let districtSearch: AnyObject
const initPage = () => {
  MD.loadAMap().then(AMap => {
    map = new AMap.Map('map', {
      // 设置地图容器id
      viewMode: '3D', // 是否为3D地图模式
      resizeEnable: true,
      // 初始化地图中心点位置
      center: [116.30946, 39.937629],
      zoom: 3 // 初始化地图级别
    })

    // @ts-ignore
    window.__MAP__ = map

    const geolocation = new AMap.Geolocation({
      enableHighAccuracy: true, //是否使用高精度定位，默认:true
      timeout: 10000, //超过10秒后停止定位，默认：5s
      position: 'RB', //定位按钮的停靠位置
      offset: [10, 20], //定位按钮与设置的停靠位置的偏移量，默认：[10, 20]
      zoomToAccuracy: true //定位成功后是否自动调整地图视野到定位点
    })
    map.addControl(geolocation)
    // @ts-ignore
    window.__GEO_LOCATION__ = geolocation

    geolocation.on('complete', (res: AnyObject) => {
      console.log(res)
    })
    geolocation.on('error', (err: AnyObject) => {
      console.log(err)
      Message('定位打开失败，检查地址是否为安全地址', 'warning')
    })

    districtSearch = new AMap.DistrictSearch({
      // 显示下级行政区级数（行政区级别包括：国家、省/直辖市、市、区/县4个级别），商圈为区/县下一 级，
      // 可选值：0、1、2、3，默认值：1
      // 0：不返回下级行政区 1：返回下一级行政区 2：返回下两级行政区 3：返回下三级行政区
      subdistrict: 1, // 返回下一级行政区
      showbiz: false // 是否显示商圈
    })

    districtSearch.setExtensions('all')
    districtSearch.search('中国', (status: string, result: AnyObject) => {
      if (status == 'complete') {
        getData(result.districtList[0], '', pageOpts.mapCode)
      }
    })
  })
}

onMounted(initPage)
</script>

<style lang="scss" module>
@use './style.scss';
</style>
